#include "Request.h"

#include <cassert>
#include <cstdlib>
#include <cstring> // for memcpy

#include "utils.h"

Request:: Request(char *buf, uint length, uint id, result_t &result):
	_buffer(buf),
 	_length(length),
 	_id(id),
 	_num_waiting(0)

{
	assert( NULL != buf );

	result = OPERATION_SUCCESS;

	if (pthread_cond_init( & _waiting, NULL)
			|| pthread_mutex_init( & _context_lock, NULL) )
	{
		result = ERROR_SYSTEM_CALL;
		return;
	}

}

bool Request:: is_someone_waiting( result_t &result )
{
	bool waiting;

	CONTEXT_LOCK_NO_RETURN( _context_lock, result );

	waiting = (_num_waiting > 0);

	CONTEXT_UNLOCK_NO_RETURN( _context_lock, result );

	return waiting;
}

result_t Request:: request_release_resources(bool force_release)
{
	result_t result = OPERATION_SUCCESS;

	if ( ! force_release )
	{
		/*
		 * we lock the context in order to make sure we are not broadcasting
		 * right before a thread enter to wait on the request so it will be hang.
		 */
		CONTEXT_LOCK_NO_RETURN( _context_lock, result );

		// wake up all the waiting threads
		if ( pthread_cond_broadcast( & _waiting ) )
		{
			ERR_PRINT("pthread_cond_broadcast() failed!\n");
			result = ERROR_SYSTEM_CALL;
		}
	}

	// release the local buffer
	if( NULL != _buffer )
	{
		DBG_PRINT("releasing the buffer #%p of request with ID #%u\n", _buffer, _id);
		free(_buffer);
		_buffer = NULL;
	}

	/*
	 * busy wait until all the waiting threads had woke up in order to make sure
	 * we allowed to delete the mutex and the conditional variable.
	 * in case we are in forced closing we don't wait.
	 */
	if ( ! force_release )
	{
		CONTEXT_UNLOCK_NO_RETURN( _context_lock , result );

		while ( is_someone_waiting( result ) )
			;
	}

	if ( pthread_cond_destroy ( & _waiting ) )
	{
		ERR_PRINT("pthread_cond_destroy() failed!\n");
		result = ERROR_SYSTEM_CALL;
	}

	if ( pthread_mutex_destroy( & _context_lock ) )
	{
		ERR_PRINT("pthread_mutex_destroy() failed!\n");
		result = ERROR_SYSTEM_CALL;
	}

	return result;
}

uint Request:: get_request_id() { return _id; };


result_t Request:: lock_request()
{
	CONTEXT_LOCK( _context_lock );

	return OPERATION_SUCCESS;
}

result_t Request:: join_request()
{
	return wait_condition_var( & _context_lock, & _waiting, & _num_waiting, true );
}

void Request:: get_buffer_info( char *& buffer, unsigned int &length )
{
	length = _length;
	buffer = _buffer;
}

